NinjaPoints = LibStub("AceAddon-3.0"):NewAddon("NinjaPoints", "AceEvent-3.0", "AceConsole-3.0")

local self = NinjaPoints

local LSM = LibStub:GetLibrary("LibSharedMedia-3.0", true)

local r, g, b, x, y
local points

local fonts = {}
local sounds = {}

local npstring = UIParent:CreateFontString("NinjaPointsString", "OVERLAY")

local newTable
local function CreateTable(index)
	newTable = {
		type = 'group',
		handler = NinjaPoints,
		name = tostring(index),
		desc = 'Configure options for combo point '..string.upper(tostring(index)),
		order = tonumber(index),
		args = {
			text = {
				type = 'input',
				name = 'Text',
				desc = function(info) return "What text you want combo point "..info.arg.." to display" end,
				get = "GetCPText",
				set = "SetCPText",
				usage = "<string>",
				width = "full",
				hidden = function(info) return self.db.profile.style[info.arg] == "number" end,
				arg = index,
			},
			style = {
				type = 'select',
				name = 'Style',
				desc = function(info) return "Style you want combo point "..info.arg.." displayed in" end,
				get = "GetCPStyle",
				set = "SetCPStyle",
				values = {["number"] = "number", ["text"] = "text", ["random"] = "random" },
				style = "dropdown",
				width = "full",
				arg = index,
			}, 
            randomColor = {
                type = 'toggle',
                name = 'randomColor',
                desc = function(info) return "Toggle coloring combo point "..info.arg.." a different color every time it appears" end,
                get = "GetCPRandomColor",
                set = "SetCPRandomColor",
                arg = index,
            }, 
			color = {
				type = 'color',
				name = 'Color',
				desc = function(info) return "Color you want combo point "..info.arg.." displayed in" end,
				get = "GetCPColor",
				set = "SetCPColor",
				hidden = function(info) return self.db.profile.randomColor[info.arg] end,
				arg = index,
			},
			sound = {
				type = 'select',
				name = 'Sound',
				desc = function(info) return "What sound you want to play when you obtain combo point "..info.arg end,
				get = "GetCPSound",
				set = "SetCPSound",
				style = "dropdown",
				width = "full",
				values = sounds,
				arg = index,
			},
		},
	}
	return newTable
end

local options = {
	type = 'group',
	name = 'NinjaPoints',
	handler = NinjaPoints,
	desc = 'Displays combo points in large, annoying numbers or words',
	args = {
		font = {
			type = 'group',
			name = 'Font',
			desc = 'Change font options',
			order = 1,
			args = {
				face = {
					type = 'select',
					name = 'Face',
					desc = 'Set combo point text font face',
					get = "GetFontFace",
					set = "SetFontFace",
					order = 1,
					style = "dropdown",
					values = fonts,
					width = "full",
				},
				size = {
					type = 'range',
					name = 'Size',
					desc = 'Set combo point font size',
					get = "GetFontSize",
					set = "SetFontSize",
					min = 8,
					max = 38,
					step = 1,
					order = 2,
					width = "full",
				},
				outline = {
					type = 'select',
					name = 'Outline',
					desc = 'Set font outline',
					get = "GetFontOutline",
					set = "SetFontOutline",
					order = 3,
					values = { ["NONE"] = "NONE", ["MONOCHROME"] = "MONOCHROME", ["OUTLINE"] = "OUTLINE", ["THICKOUTLINE"] = "THICKOUTLINE" },
					style = "dropdown",
					width = "full",
				},
			},
		},
		test = {
			type = 'execute',
			name = 'Test',
			desc = 'Test combo point text frame',
			func = "ToggleTestFrame",
			order = 7,
		},
		fade = {
			type = 'group',
			name = 'Fade',
			desc = 'Configure fading options',
			args = {
				enable = {
					type = 'toggle',
					name = 'Enable',
					desc = 'Enable fading',
					get = "GetFading",
					set = "SetFading",
				},
				timeIn = {
					type = 'range',
					name = 'Time in',
					desc = 'Amount of time (in seconds) for the frame to fade in',
					get = "GetFadeTimeIn",
					set = "SetFadeTimeIn",
					min = 0.1,
					max = 3.0,
					step = 0.1,
					width = "full",
				},
				timeOut = {
					type = 'range',
					name = 'Time out',
					desc = 'Amount of time (in seconds) for the frame to fade out',
					get = "GetFadeTimeOut",
					set = "SetFadeTimeOut",
					min = 0.1,
					max = 3.0,
					step = 0.1,
					width = "full",
				},
			},
		},
		points = {
			type = 'group',
			name = 'Points',
			desc = 'Configure what to show for combo points',
			args = {
				one = CreateTable(1),
				two = CreateTable(2),
				three = CreateTable(3),
				four = CreateTable(4),
				five = CreateTable(5),
			},
			order = 3,
		},
		xOff = {
			type = 'input',
			name = 'xOff',
			desc = 'Set X offset for combo point text',
			order = 4,
			get = "GetXOffset",
			set = "SetXOffset",
		},
		yOff = {
			type = 'input',
			name = 'yOff',
			desc = 'Set Y offset for combo point text',
			order = 5,
			get = "GetYOffset",
			set = "SetYOffset",
		},
		reset = {
			type = 'execute',
			name = 'Reset',
			desc = 'Reset options back to their defaults',
			order = 8,
			func = "ResetDB",
			confirm = true,
		},
	},
}

local defaultDB = {
	profile = {
		fontFace = "Fonts\\FRIZQT__.ttf",
		fontSize = 36,
		fontOutline = "NONE",
		xOff = 0,
		yOff = 400,
		fade = {
			enable = true,
			timeIn = "0.2",
			timeOut = "0.5",
		},
		style = {
			[1] = "number",
			[2] = "number",
			[3] = "number",
			[4] = "number",
			[5] = "number",
		},
		randomColor = {
			[1] = false,
			[2] = false,
			[3] = false,
			[4] = false,
			[5] = false,
		},
		sounds = {
			[1] = "None",
			[2] = "None",
			[3] = "None",
			[4] = "None",
			[5] = "None",
		},
		texts = {
			[1] = "ONE",
			[2] = "TWO",
			[3] = "THREE",
			[4] = "FOUR",
			[5] = "FIVE",
		},
		colors = {
			[1] = {
				red = ".17",
				green = ".90",
				blue = ".11",
			},
			[2] = {
				red = ".18",
				green = ".93",
				blue = ".93",
			},
			[3] = {
				red = ".89",
				green = ".93",
				blue = ".18",
			},
			[4] = {
				red = ".22",
				green = ".18",
				blue = ".80",
			},
			[5] = {
				red = "1.00",
				green = ".0",
				blue = ".0",
			},
		},
	},
}

function NinjaPoints:ChatCommand(input)
	if not input or input:trim() == "" then
        InterfaceOptionsFrame_OpenToFrame(self.optionsFrame)
    else
        LibStub("AceConfigCmd-3.0").HandleCommand(NinjaPoints, "np", "NinjaPoints", input)
    end
end

function NinjaPoints:OnInitialize()
	self.db = LibStub("AceDB-3.0"):New("NinjaPointsDB", defaultDB, "Defaults")
	
	if LSM then
		LSM.RegisterCallback(NinjaPoints, "LSMNew")
		
		options.args.font.args.face.values = fonts
		options.args.points.args.one.args.sound.values = sounds
		options.args.points.args.two.args.sound.values = sounds
		options.args.points.args.three.args.sound.values = sounds
		options.args.points.args.four.args.sound.values = sounds
		options.args.points.args.five.args.sound.values = sounds
	else
		options.args.font.args.face.values = self.Fonts
		options.args.points.args.one.args.sound.values = self.Sounds
		options.args.points.args.two.args.sound.values = self.Sounds
		options.args.points.args.three.args.sound.values = self.Sounds
		options.args.points.args.four.args.sound.values = self.Sounds
		options.args.points.args.five.args.sound.values = self.Sounds
	end

	LibStub("AceConfig-3.0"):RegisterOptionsTable("NinjaPoints", options)
	self.optionsFrame = LibStub("AceConfigDialog-3.0"):AddToBlizOptions("NinjaPoints", "NinjaPoints")

	self:RegisterChatCommand("npoints", "ChatCommand")
	self:RegisterChatCommand("np", "ChatCommand")

	if LSM then
		LSM:Register("sound", "Finish Him!", "Interface\\AddOns\\NinjaPoints\\Sounds\\FinishHim.wav")
	end
end

function NinjaPoints:OnEnable()
	self:RegisterEvent("PLAYER_COMBO_POINTS")
	self:LSMNew()
end

function NinjaPoints:LSMNew()
	if not LSM then return end
	for k, v in pairs(LSM:List("font")) do
		fonts[v] = v
	end

	for k, v in pairs(LSM:List("sound")) do
		sounds[v] = v
	end
end

function NinjaPoints:PLAYER_COMBO_POINTS()
	points = GetComboPoints()
	if ( points == 0 ) then self:SetStringVisible(false) return end
	r, g, b = self:GetTextColors(points)

	self:SetFont(self.db.profile.fontFace or "Fonts\\FRIZQT__.TTF")
	if self.db.profile.style[points] == "number" then
		npstring:SetText(points)
	elseif self.db.profile.style[points] == "random" then
		local roll = random(1,2)
		if roll > 1 then
			npstring:SetText(self.db.profile.texts[points])
		else
			npstring:SetText(points)
		end
	elseif self.db.profile.style[points] == "text" then
		npstring:SetText(self.db.profile.texts[points])
	end
	
	npstring:SetPoint("CENTER", UIParent, "CENTER", self.db.profile.xOff, self.db.profile.yOff)
	npstring:SetTextColor(r, g, b, 1.0)
	if not npstring:IsVisible() then
		self:SetStringVisible(true)
	end

	self:PlaySound(self.db.profile.sounds[points])
end

function NinjaPoints:ResetDB()
	self.db = defaultDB
	npstring:SetPoint("CENTER", UIParent, "CENTER", self.db.profile.xOff, self.db.profile.yOff)
	self:Print("NinjaPointsDB reset to original values")
end

function NinjaPoints:ToggleTestFrame()
	if npstring:IsVisible() and GetComboPoints() == 0 then
		self:SetStringVisible(false)
	else
		points = math.random(1, 5)
		r, g, b = self:GetTextColors(points)
		self:SetFont(self.db.profile.fontFace)	
		if self.db.profile.style[points] == "number" then
			npstring:SetText(points)
		elseif self.db.profile.style[points] == "random" then
			local roll = random(1,2)
			if roll > 1 then
				npstring:SetText(self.db.profile.texts[points])
			else
				npstring:SetText(points)
			end
		elseif self.db.profile.style[points] == "text" then
			npstring:SetText(self.db.profile.texts[points])
		end

		npstring:SetPoint("CENTER", UIParent, "CENTER", self.db.profile.xOff, self.db.profile.yOff)
		npstring:SetTextColor(r, g, b)
		self:SetStringVisible(true)

		self:PlaySound(self.db.profile.sounds[points])
	end
end

function NinjaPoints:RegisterSound(name, soundpath)
	if not self.Sounds then
		self.Sounds = { }
	end

	self.Sounds[soundpath] = name
end

function NinjaPoints:RegisterFont(name, fontpath)
	if not self.Fonts then
		self.Fonts = { }
	end

	self.Fonts[fontpath] = name
end

function NinjaPoints:SetFont(font)
	if LSM then
		npstring:SetFont(LSM:Fetch("font", font), self.db.profile.fontSize, self.db.profile.fontOutline)
	else
		npstring:SetFont(font, self.db.profile.fontSize, self.db.profile.fontOutline)
	end
end

function NinjaPoints:PlaySound(sound)
	if LSM then
		PlaySoundFile(LSM:Fetch("sound", sound))
	else
		PlaySoundFile(sound)
	end
end

function NinjaPoints:SetStringVisible(value)
	if self.db.profile.fade.enable then
		if value then
			UIFrameFadeIn(npstring, tonumber(self.db.profile.fade.timeIn), 0, 1)
		else
			UIFrameFadeOut(npstring, tonumber(self.db.profile.fade.timeOut), 1, 0)
		end
		npstring.fadeInfo.finishedFunc = function() self:SetVisible(npstring, value) end
	else
		self:SetVisible(npstring, value)
	end
end

function NinjaPoints:GetTextColors(index)
	if self.db.profile.randomColor[index] then
		r = math.random()
		g = math.random()
		b = math.random()
	else
		r = self.db.profile.colors[index].red
		g = self.db.profile.colors[index].green
		b = self.db.profile.colors[index].blue
	end
	return r, g, b
end

function NinjaPoints:SetVisible(frame, show)
	if show then
		frame:Show()
	else
		frame:Hide()
	end
end

function NinjaPoints:GetCPText(info)
	return self.db.profile.texts[info.arg]
end

function NinjaPoints:SetCPText(info, value)
	self.db.profile.texts[info.arg] = tostring(value)
end

function NinjaPoints:GetCPStyle(info)
	return self.db.profile.style[info.arg]
end

function NinjaPoints:SetCPStyle(info, value)
	self.db.profile.style[info.arg] = value
end

function NinjaPoints:GetCPRandomColor(info)
	return self.db.profile.randomColor[info.arg]
end

function NinjaPoints:SetCPRandomColor(info, value)
	self.db.profile.randomColor[info.arg] = value
end

function NinjaPoints:GetCPColor(info)
	return self.db.profile.colors[info.arg].red, self.db.profile.colors[info.arg].green, self.db.profile.colors[info.arg].blue
end

function NinjaPoints:SetCPColor(info, r, g, b)
	self.db.profile.colors[info.arg] = {red = r, green = g, blue = b}
end

function NinjaPoints:GetCPSound(info)
	return self.db.profile.sounds[info.arg]
end

function NinjaPoints:SetCPSound(info, value)
	self.db.profile.sounds[info.arg] = value
	self:PlaySound(value)
end

function NinjaPoints:GetFontFace(info)
	return self.db.profile.fontFace
end

function NinjaPoints:SetFontFace(info, value)
	self.db.profile.fontFace = value
	self:SetFont(value)
end

function NinjaPoints:GetFontSize(info)
	return self.db.profile.fontSize
end

function NinjaPoints:SetFontSize(info, value)
	self.db.profile.fontSize = value
	self:SetFont(self.db.profile.fontFace)
end

function NinjaPoints:GetFontOutline(info)
	return self.db.profile.fontOutline
end

function NinjaPoints:SetFontOutline(info, value)
	self.db.profile.fontOutline = value
	self:SetFont(self.db.profile.fontFace)
end

function NinjaPoints:GetXOffset(info)
	return self.db.profile.xOff
end

function NinjaPoints:SetXOffset(info, value)
	self.db.profile.xOff = value
	npstring:SetPoint("CENTER", UIParent, "CENTER", value, (self.db.profile.yOff or 0))	
end

function NinjaPoints:GetYOffset(info)
	return self.db.profile.yOff
end

function NinjaPoints:SetYOffset(info, value)
	self.db.profile.yOff = value
	npstring:SetPoint("CENTER", UIParent, "CENTER", (self.db.profile.xOff or 0), value)	
end

function NinjaPoints:GetFading(info)
	return self.db.profile.fade.enable
end

function NinjaPoints:SetFading(info, value)
	self.db.profile.fade.enable = value
end

function NinjaPoints:GetFadeTimeIn(info)
	return self.db.profile.fade.timeIn
end

function NinjaPoints:SetFadeTimeIn(info, value)
	self.db.profile.fade.timeIn = value
end

function NinjaPoints:GetFadeTimeOut(info)
	return self.db.profile.fade.timeOut
end

function NinjaPoints:SetFadeTimeOut(info, value)
	self.db.profile.fade.timeOut = value
end